import { Injectable } from '@angular/core'
import { Observable, of } from 'rxjs'
import { map, concatMap } from 'rxjs/operators'
import { NzTreeNode, NzTreeNodeOptions } from 'ng-zorro-antd'

import { IMenu } from '@shared/interfaces/role'
import { TreeService } from '@shared/services/tree.service'
import { MenuService } from './menu.service'

@Injectable()
export class MenuDataService {
  _menus: IMenu[]

  constructor(
    private menuService: MenuService,
    private treeService: TreeService,
  ) { }

  getSlice(ordinal: number, menus: IMenu[]): [IMenu[], number] {
    let startIndex = -1
    const menu = menus.find((ele, idx) => {
      if (ele.ordinal === ordinal) {
        startIndex = idx
        return true
      }

      return false
    })

    const slice: IMenu[] = [menus[startIndex]]
    for (let i = startIndex + 1; i < menus.length; i++) {
      const current = menus[i]
      if (current.level > menu.level) {
        slice.push({...current})
      } else {
        break
      }
    }

    return [slice, startIndex]
  }

  createOptionTree(menus: IMenu[]): NzTreeNodeOptions[] {
    const optionsMap: { [key: string]: NzTreeNodeOptions } = {}
    const size = menus.length
    const options: NzTreeNodeOptions[] = menus.map(m => {
      const option = {
        title: `${m.menuName}:${m.menuUri}`,
        key: `${m.ordinal}`,
        isLeaf: true,
        menuId: m.menuId,
        menuLevel: m.level,
        menuOrdinal: m.ordinal,
      }

      optionsMap[m.ordinal] = option

      return option
    })

    let i = 0
    let previousLevel = -1
    while (i < size) {
      const { menuLevel: level, menuOrdinal: ordinal } = options[i]
      const option = optionsMap[ordinal]
      if (i === 0) {
        option.level = 0
        previousLevel = option.menuLevel
        i++
        continue
      }

      const previous = optionsMap[options[i - 1].menuOrdinal]
      if (level === 1) {
        option.level = 0
      } else if (level > previousLevel) {
        // 当前节点为前一个节点的孩子
        option.menuOrdinalParent = previous.menuOrdinal
        previous.children = previous.children || []
        previous.children.push(option)
      } else if (level === previousLevel) {
        // 当前节点为前一个节点的兄弟
        const parent = optionsMap[previous.menuOrdinalParent]
        option.menuOrdinalParent = parent.menuOrdinal
        parent.children.push(option)
      } else {
        // 当前节点为前一个节点的叔叔
        const sibling = optionsMap[previous.menuOrdinalParent]
        const parent = optionsMap[sibling.menuOrdinalParent]
        if (parent) {
          option.menuOrdinalParent = parent.menuOrdinal
          parent.children.push(option)
        }
      }

      previousLevel = option.menuLevel
      i++
    }


    return Object.values(optionsMap)
    .map(option => {
      if (option.children && option.children.length) {
        option.isLeaf = false
        // option.expanded = true
      }

      return option
    })
    .filter(option => option.level === 0)
  }

  copyMenu(ordinal: number, menus: IMenu[]): IMenu[] {
    const [slice, startIndex] = this.getSlice(ordinal, menus)

    menus.splice(startIndex + slice.length, 0, ...slice)
    return menus.map((item, idx) => {
      item.ordinal = idx + 1
      return {...item}
    }).sort((a, b) => (a.ordinal - b.ordinal))
  }

  removeMenu(ordinal: number, menus: IMenu[]): IMenu[] {
    const [slice, startIndex] = this.getSlice(ordinal, menus)

    menus.splice(startIndex, slice.length)
    return menus.map((item, idx) => {
      item.ordinal = idx + 1
      return {...item}
    }).sort((a, b) => (a.ordinal - b.ordinal))
  }

  // if isFromServer is true, will load menus from server
  fetchMenuTree(isFromServer: boolean): Observable<NzTreeNode[]> {
    return of(this._menus).pipe(
      concatMap(menus => {
        if (!isFromServer && menus && menus.length) {
          return of(menus)
        }

        return this.menuService.getMenus()
      }),
      map(menus => {
        this._menus = menus.slice(0)
        const tree = this.treeService.createTree(menus)
        return tree
      })
    )
  }

  getAncestors(node: NzTreeNode): NzTreeNode[] {
    let parent = node.getParentNode()
    const ancestors: NzTreeNode[] = []
    while (parent) {
      ancestors.push(parent)
      parent = parent.getParentNode()
    }

    return ancestors
  }
}
